var BENCODE = {};

Object.defineProperty(BENCODE, 'stringify', {
	configurable: false,
	enumerable: true,
	writable: false,
	value: function(object) {
		if ('string' == typeof object) {
			return object.length + ':' + object;
		}
		if ('number' == typeof object) {
			return 'i' + object + 'e';
		}
		if (object instanceof Array) {
			var ret = 'l';
			for (var i in object)
				ret += this.stringify(object[i]);
			return ret + 'e';
		}
		if (object instanceof Object) {
			var ret = 'd';
			for (var i in object)
				ret += this.stringify(i) + this.stringify(object[i]);
			return ret + 'e';
		}
	}
});

Object.defineProperty(BENCODE, 'parse', {
	configurable: false,
	enumerable: true,
	writable: false,
	value: function(string) {
		return this._parse(string, 0).object;
	}
});

Object.defineProperty(BENCODE, '_parse', {
	configurable: false,
	enumerable: false,
	writable: false,
	value: function(string, i) {
		if (i >= string.length)
			throw new SyntaxError('Unexpected end of string at ' + (string.length - 1));
		if (string[i] == 'i') {
			var found = false;
			for (var j = i + 1; j < string.length; ++j) {
				if (string[j] == 'e') {
					found = true;
					break;
				}
				if (!/[\-0-9]/.test(string[j]))
					throw new SyntaxError('Unexpected character at ' + j + '. Expected \'e\', found \'' + string[j] + '\'');
			}
			if (!found)
				throw new SyntaxError('Unexpected end of string at ' + j);
			var integer = string.substr(i + 1, j - i - 1);
			if (/^(0|(\-?[1-9])|(\-?[1-9]+[0-9]+))$/.test(integer))
				integer = parseInt(integer);
			else
				throw new SyntaxError('Invalid integer at ' + i);
			return {object: integer, offset: j + 1};
		}
		if (string[i] == 'l') {
			++i;
			var ret = [];
			while (i < string.length) {
				if (string[i] == 'e')
					return {object: ret, offset: i + 1};
				var element = this._parse(string, i);
				i = element.offset;
				ret.push(element.object);
			}
			throw new SyntaxError('Unexpected end of string at ' + i);
		}
		if (string[i] == 'd') {
			++i;
			var ret = {};
			while (i < string.length) {
				if (string[i] == 'e')
					return {object: ret, offset: i + 1};
				var key = this._parse(string, i);
				if (typeof key.object != 'string')
					throw new SyntaxError('Unexpected object type at ' + i + '. Expected string, found ' + (typeof key.object));
				i = key.offset;
				var value = this._parse(string, i);
				i = value.offset;
				ret[key.object] = value.object;
			}
		}
		if (/[0-9]/.test(string[i])) {
			var found = false;
			for (var j = i + 1; j < string.length; ++j) {
				if (string[j] == ':') {
					found = true;
					break;
				}
				if (!/[0-9]/.test(string[j]))
					throw new SyntaxError('Unexpected character found at ' + j + '. Expected numeral, found ' + string[j]);
			}
			if (!found)
				throw new SyntaxError('Unexpected end of string at ' + i);
			var length = parseInt(string.substr(i, j - i));
			i = j + 1;
			if (i + length > string.length)
				throw new SyntaxError('Unexpected end of string at ' + (string.length - 1));
			return {object: string.substr(i, length), offset: i + length};
		}
		throw new SyntaxError('Unexpected character at ' + i);
	}
});

module.exports = BENCODE;